/*      */ package com.zimbra.cs.db;
/*      */ 
/*      */ import com.google.common.annotations.VisibleForTesting;
/*      */ import com.google.common.base.Joiner;
/*      */ import com.google.common.base.Strings;
/*      */ import com.google.common.collect.Lists;
/*      */ import com.google.common.collect.Maps;
/*      */ import com.google.common.collect.Sets;
/*      */ import com.zimbra.common.localconfig.DebugConfig;
/*      */ import com.zimbra.common.mailbox.Color;
/*      */ import com.zimbra.common.service.ServiceException;
/*      */ import com.zimbra.common.util.ArrayUtil;
/*      */ import com.zimbra.common.util.Log;
/*      */ import com.zimbra.common.util.ZimbraLog;
/*      */ import com.zimbra.cs.mailbox.Flag;
/*      */ import com.zimbra.cs.mailbox.Folder;
/*      */ import com.zimbra.cs.mailbox.MailItem;
/*      */ import com.zimbra.cs.mailbox.MailItem.PendingDelete;
/*      */ import com.zimbra.cs.mailbox.MailItem.Type;
/*      */ import com.zimbra.cs.mailbox.MailItem.UnderlyingData;
/*      */ import com.zimbra.cs.mailbox.MailServiceException;
/*      */ import com.zimbra.cs.mailbox.Mailbox;
/*      */ import com.zimbra.cs.mailbox.MailboxLock;
/*      */ import com.zimbra.cs.mailbox.Metadata;
/*      */ import com.zimbra.cs.mailbox.RetentionPolicyManager;
/*      */ import com.zimbra.cs.mailbox.Tag;
/*      */ import com.zimbra.soap.mail.type.RetentionPolicy;
/*      */ import java.sql.PreparedStatement;
/*      */ import java.sql.ResultSet;
/*      */ import java.sql.SQLException;
/*      */ import java.util.ArrayList;
/*      */ import java.util.Collection;
/*      */ import java.util.Collections;
/*      */ import java.util.HashMap;
/*      */ import java.util.Iterator;
/*      */ import java.util.List;
/*      */ import java.util.Map;
/*      */ import java.util.Set;
/*      */ import junit.framework.Assert;
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ public final class DbTag
/*      */ {
/*      */   public static final String TABLE_TAG = "tag";
/*      */   public static final String TABLE_TAGGED_ITEM = "tagged_item";
/*      */   private static final String TAG_FIELDS = "id, name, color, item_count, unread, listed, sequence, policy";
/*      */   private static final String TAG_SEPARATOR = "\000";
/*      */   
/*      */   private static MailItem.UnderlyingData asUnderlyingData(ResultSet rs)
/*      */     throws SQLException, ServiceException
/*      */   {
/*   66 */     MailItem.UnderlyingData data = new MailItem.UnderlyingData();
/*   67 */     data.id = rs.getInt(1);
/*   68 */     data.type = MailItem.Type.TAG.toByte();
/*   69 */     data.folderId = 8;
/*   70 */     data.name = rs.getString(2);
/*   71 */     data.size = rs.getInt(4);
/*   72 */     data.unreadCount = rs.getInt(5);
/*      */     
/*   74 */     boolean listed = rs.getBoolean(6);
/*   75 */     String policy = rs.getString(8);
/*   76 */     RetentionPolicy rp = policy == null ? null : RetentionPolicyManager.retentionPolicyFromMetadata(new Metadata(policy), true);
/*   77 */     long c = rs.getLong(3);
/*   78 */     Color color = rs.wasNull() ? null : Color.fromMetadata(c);
/*      */     
/*   80 */     data.metadata = Tag.encodeMetadata(color, 1, 1, rp, listed);
/*      */     
/*   82 */     data.modMetadata = rs.getInt(7);
/*   83 */     data.modContent = data.modMetadata;
/*   84 */     return data;
/*      */   }
/*      */   
/*      */ 
/*   88 */   private static final Joiner TAG_JOINER = Joiner.on("\000").skipNulls();
/*      */   
/*      */   public static String serializeTags(String[] tags) {
/*   91 */     if ((tags == null) || (tags.length == 0)) {
/*   92 */       return null;
/*      */     }
/*      */     
/*   95 */     StringBuilder encoded = TAG_JOINER.appendTo(new StringBuilder("\000"), tags);
/*   96 */     return "\000";
/*      */   }
/*      */   
/*      */   public static String[] deserializeTags(String encoded) {
/*  100 */     if ((encoded == null) || (encoded.isEmpty())) {
/*  101 */       return null;
/*      */     }
/*  103 */     return encoded.substring(encoded.charAt(0) == 0 ? 1 : 0).split("\000");
/*      */   }
/*      */   
/*      */   private static String delimitTagName(String name) {
/*  107 */     return "\000" + name + "\000";
/*      */   }
/*      */   
/*      */   static String tagLIKEPattern(String tagName) {
/*  111 */     return '%' + delimitTagName(tagName).replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") + '%';
/*      */   }
/*      */   
/*      */   public static void createTag(Mailbox mbox, MailItem.UnderlyingData data, Color color, boolean listed) throws ServiceException {
/*  115 */     createTag(mbox.getOperationConnection(), mbox, data, color, listed);
/*      */   }
/*      */   
/*      */   public static void createTag(DbPool.DbConnection conn, Mailbox mbox, MailItem.UnderlyingData data, Color color, boolean listed) throws ServiceException {
/*  119 */     PreparedStatement stmt = null;
/*      */     try {
/*  121 */       stmt = conn.prepareStatement("INSERT INTO " + getTagTableName(mbox) + " (" + DbMailItem.MAILBOX_ID + " id, name, color, listed, sequence)" + " VALUES (" + DbMailItem.MAILBOX_ID_VALUE + "?, ?, ?, ?, ?)");
/*      */       
/*      */ 
/*  124 */       int pos = 1;
/*  125 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  126 */       stmt.setInt(pos++, data.id);
/*  127 */       stmt.setString(pos++, data.name);
/*  128 */       if (color != null) {
/*  129 */         stmt.setLong(pos++, color.getValue());
/*      */       } else {
/*  131 */         stmt.setNull(pos++, -5);
/*      */       }
/*  133 */       stmt.setBoolean(pos++, listed);
/*  134 */       stmt.setInt(pos++, data.modMetadata);
/*      */       
/*  136 */       int num = stmt.executeUpdate();
/*  137 */       if (num != 1) {
/*  138 */         throw ServiceException.FAILURE("failed to create object", null);
/*      */       }
/*      */     }
/*      */     catch (SQLException e) {
/*  142 */       if (Db.errorMatches(e, Db.Error.DUPLICATE_ROW)) {
/*  143 */         throw MailServiceException.ALREADY_EXISTS(data.id, e);
/*      */       }
/*  145 */       throw ServiceException.FAILURE("Failed to create tag id=" + data.id + ",name=" + data.name, e);
/*      */     }
/*      */     finally {
/*  148 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static MailItem.UnderlyingData getTag(Mailbox mbox, String name) throws ServiceException {
/*  153 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  154 */     PreparedStatement stmt = null;
/*  155 */     ResultSet rs = null;
/*      */     try {
/*  157 */       stmt = conn.prepareStatement("SELECT id, name, color, item_count, unread, listed, sequence, policy FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "name = ?");
/*      */       
/*  159 */       int pos = 1;
/*  160 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  161 */       stmt.setString(pos++, name);
/*      */       
/*  163 */       rs = stmt.executeQuery();
/*  164 */       return rs.next() ? asUnderlyingData(rs) : null;
/*      */     } catch (SQLException e) {
/*  166 */       throw ServiceException.FAILURE("fetching tag " + name, e);
/*      */     } finally {
/*  168 */       DbPool.closeResults(rs);
/*  169 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static List<MailItem.UnderlyingData> getUnreadMessages(Tag tag) throws ServiceException {
/*  174 */     Mailbox mbox = tag.getMailbox();
/*      */     
/*  176 */     ArrayList<MailItem.UnderlyingData> result = new ArrayList();
/*      */     
/*  178 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  179 */     PreparedStatement stmt = null;
/*  180 */     ResultSet rs = null;
/*      */     try {
/*  182 */       String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
/*  183 */       stmt = conn.prepareStatement("SELECT mi.id, mi.type, mi.parent_id, mi.folder_id, mi.prev_folders, mi.index_id,mi.imap_id, mi.date, mi.size, mi.locator, mi.blob_digest, mi.unread, mi.flags, mi.tag_names, mi.subject,mi.name, mi.metadata, mi.mod_metadata, mi.change_date, mi.mod_content, mi.uuid FROM " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "ti.item_id = mi.id" + " WHERE " + inThisMailboxAnd("ti") + "type NOT IN " + DbMailItem.NON_SEARCHABLE_TYPES + " AND unread > 0 AND ti.tag_id = ?");
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*  188 */       if (tag.getUnreadCount() > 10000) {
/*  189 */         Db.getInstance().enableStreaming(stmt);
/*      */       }
/*  191 */       int pos = 1;
/*  192 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  193 */       stmt.setInt(pos++, tag.getId());
/*      */       
/*  195 */       rs = stmt.executeQuery();
/*  196 */       MailItem.UnderlyingData data; while (rs.next()) {
/*  197 */         data = DbMailItem.constructItem(rs);
/*  198 */         if (Mailbox.isCachedType(MailItem.Type.of(data.type))) {
/*  199 */           throw ServiceException.INVALID_REQUEST("folders and tags must be retrieved from cache", null);
/*      */         }
/*  201 */         result.add(data);
/*      */       }
/*  203 */       return result;
/*      */     } catch (SQLException e) {
/*  205 */       throw ServiceException.FAILURE("fetching unread messages for tag " + tag.getName(), e);
/*      */     } finally {
/*  207 */       DbPool.closeResults(rs);
/*  208 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   static boolean getAllTags(Mailbox mbox, DbMailItem.FolderTagMap tagData) throws ServiceException {
/*  213 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  214 */     PreparedStatement stmt = null;
/*  215 */     ResultSet rs = null;
/*      */     try {
/*  217 */       stmt = conn.prepareStatement("SELECT id, name, color, item_count, unread, listed, sequence, policy FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id > 0");
/*      */       
/*  219 */       DbMailItem.setMailboxId(stmt, mbox, 1);
/*      */       
/*  221 */       rs = stmt.executeQuery();
/*  222 */       while (rs.next()) {
/*  223 */         tagData.put(asUnderlyingData(rs), null);
/*      */       }
/*  225 */       return false;
/*      */     } catch (SQLException e) {
/*  227 */       throw ServiceException.FAILURE("fetching all tags for mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  229 */       DbPool.closeResults(rs);
/*  230 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static MailItem.PendingDelete getImapDeleted(Mailbox mbox, Set<Folder> folders) throws ServiceException {
/*  235 */     if ((folders != null) && (folders.isEmpty())) {
/*  236 */       return new MailItem.PendingDelete();
/*      */     }
/*      */     
/*  239 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  240 */     PreparedStatement stmt = null;
/*  241 */     ResultSet rs = null;
/*      */     try {
/*  243 */       String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
/*  244 */       String folderconstraint = " AND " + DbUtil.whereIn("folder_id", folders.size());
/*  245 */       stmt = conn.prepareStatement("SELECT id, size, type, unread, folder_id, parent_id, blob_digest, mod_content, mod_metadata, flags, index_id, locator, tag_names, uuid FROM " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "ti.item_id = mi.id" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = " + Flag.ID_DELETED + " AND type IN " + DbMailItem.IMAP_TYPES + folderconstraint);
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*  250 */       if (DbMailItem.getTotalFolderSize(folders) > 10000) {
/*  251 */         Db.getInstance().enableStreaming(stmt);
/*      */       }
/*  253 */       int pos = 1;
/*  254 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  255 */       Iterator i$; if (folders != null) {
/*  256 */         for (i$ = folders.iterator(); i$.hasNext();) { folder = (Folder)i$.next();
/*  257 */           stmt.setInt(pos++, folder.getId());
/*      */         }
/*      */       }
/*      */       Folder folder;
/*  261 */       MailItem.PendingDelete info = DbMailItem.accumulateDeletionInfo(mbox, stmt);
/*  262 */       stmt = null;
/*  263 */       return info;
/*      */     } catch (SQLException e) {
/*  265 */       throw ServiceException.FAILURE("fetching list of \\Deleted items for purge", e);
/*      */     } finally {
/*  267 */       DbPool.closeResults(rs);
/*  268 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   static void recalculateTagCounts(Mailbox mbox, Map<Integer, MailItem.UnderlyingData> lookup) throws ServiceException {
/*  273 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  274 */     PreparedStatement stmt = null;
/*  275 */     ResultSet rs = null;
/*      */     try {
/*  277 */       String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
/*  278 */       stmt = conn.prepareStatement("SELECT ti.tag_id, COUNT(*), " + Db.clauseIFNULL("SUM(mi.unread)", "0") + " FROM " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "ti.item_id = mi.id" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id > 0 AND type NOT IN " + DbMailItem.NON_SEARCHABLE_TYPES + " AND " + Db.getInstance().bitAND("mi.flags", new StringBuilder().append("").append(Flag.BITMASK_DELETED).toString()) + " = 0" + " GROUP BY ti.tag_id");
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  284 */       DbMailItem.setMailboxId(stmt, mbox, 1);
/*      */       
/*  286 */       rs = stmt.executeQuery();
/*  287 */       while (rs.next()) {
/*  288 */         int tagId = rs.getInt(1);
/*  289 */         int count = rs.getInt(2);
/*  290 */         int unread = rs.getInt(3);
/*      */         
/*  292 */         MailItem.UnderlyingData data = (MailItem.UnderlyingData)lookup.get(Integer.valueOf(tagId));
/*  293 */         if (data != null) {
/*  294 */           data.unreadCount += unread;
/*  295 */           data.size += count;
/*      */         } else {
/*  297 */           ZimbraLog.mailbox.warn("inconsistent DB state: items with no corresponding tag (tag id %d)", new Object[] { Integer.valueOf(tagId) });
/*      */         }
/*      */       }
/*      */     } catch (SQLException e) {
/*  301 */       throw ServiceException.FAILURE("recalculating tag counts for mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  303 */       DbPool.closeResults(rs);
/*  304 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   static void updateTagReferences(Mailbox mbox, int itemId, MailItem.Type type, int flags, boolean unread, String[] tags) throws ServiceException {
/*  309 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  310 */     PreparedStatement stmt = null;
/*      */     try {
/*  312 */       stmt = conn.prepareStatement("DELETE FROM " + getTaggedItemTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "item_id = ?");
/*      */       
/*  314 */       int pos = 1;
/*  315 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  316 */       stmt.setInt(pos++, itemId);
/*  317 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  319 */       throw ServiceException.FAILURE("storing flag references in mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  321 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */     
/*  324 */     storeTagReferences(mbox, itemId, type, flags, unread);
/*  325 */     storeTagReferences(mbox, itemId, type, tags);
/*      */   }
/*      */   
/*      */   static void storeTagReferences(Mailbox mbox, int itemId, MailItem.Type type, int flags, boolean unread) throws ServiceException {
/*  329 */     if (!type.isLeafNode()) {
/*  330 */       return;
/*      */     }
/*  332 */     if (unread) {
/*  333 */       flags |= Flag.BITMASK_UNREAD;
/*  334 */     } else if (flags == 0) {
/*  335 */       return;
/*      */     }
/*      */     
/*  338 */     List<Integer> flagIds = Lists.newArrayList();
/*  339 */     for (Iterator i$ = Mailbox.REIFIED_FLAGS.iterator(); i$.hasNext();) { int tagId = ((Integer)i$.next()).intValue();
/*  340 */       if ((flags & 1 << -tagId - 1) != 0) {
/*  341 */         flagIds.add(Integer.valueOf(tagId));
/*      */       }
/*      */     }
/*  344 */     if (flagIds.isEmpty()) {
/*  345 */       return;
/*      */     }
/*  347 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  348 */     PreparedStatement stmt = null;
/*      */     try {
/*  350 */       stmt = conn.prepareStatement("INSERT INTO " + getTaggedItemTableName(mbox) + "(" + DbMailItem.MAILBOX_ID + "tag_id, item_id)" + " SELECT " + DbMailItem.MAILBOX_ID + "id, ? FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + DbUtil.whereIn("id", flagIds.size()));
/*      */       
/*      */ 
/*      */ 
/*  354 */       int pos = 1;
/*  355 */       stmt.setInt(pos++, itemId);
/*  356 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  357 */       for (Iterator i$ = flagIds.iterator(); i$.hasNext();) { int flagId = ((Integer)i$.next()).intValue();
/*  358 */         stmt.setInt(pos++, flagId);
/*      */       }
/*  360 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  362 */       throw ServiceException.FAILURE("storing flag references in mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  364 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   static void storeTagReferences(Mailbox mbox, int itemId, MailItem.Type type, String[] tags) throws ServiceException {
/*  369 */     if ((!type.isLeafNode()) || (ArrayUtil.isEmpty(tags))) {
/*  370 */       return;
/*      */     }
/*  372 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  373 */     PreparedStatement stmt = null;
/*      */     try {
/*  375 */       stmt = conn.prepareStatement("INSERT INTO " + getTaggedItemTableName(mbox) + "(" + DbMailItem.MAILBOX_ID + "tag_id, item_id)" + " SELECT " + DbMailItem.MAILBOX_ID + "id, ? FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + DbUtil.whereIn("name", tags.length));
/*      */       
/*      */ 
/*      */ 
/*  379 */       int pos = 1;
/*  380 */       stmt.setInt(pos++, itemId);
/*  381 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  382 */       for (String tag : tags) {
/*  383 */         stmt.setString(pos++, tag);
/*      */       }
/*  385 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  387 */       throw ServiceException.FAILURE("storing tag references in mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  389 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void alterTag(Tag tag, List<Integer> itemIds, boolean add) throws ServiceException {
/*  394 */     if ((itemIds == null) || (itemIds.isEmpty())) {
/*  395 */       return;
/*      */     }
/*      */     
/*  398 */     Mailbox mbox = tag.getMailbox();
/*      */     
/*  400 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  401 */     PreparedStatement stmt = null;
/*      */     try {
/*  403 */       boolean isFlag = tag instanceof Flag;
/*  404 */       boolean altersModseq = (!isFlag) || (!((Flag)tag).isSystemFlag());
/*      */       String sanityCheckAnd;
/*      */       String primaryUpdate;
/*  407 */       String sanityCheckAnd; if (isFlag) {
/*  408 */         String primaryUpdate = "flags = flags" + (add ? " + ?" : " - ?");
/*  409 */         sanityCheckAnd = Db.getInstance().bitAND("flags", "?") + (add ? " = 0" : " <> 0") + " AND ";
/*      */       } else { String sanityCheckAnd;
/*  411 */         if (add) {
/*  412 */           String primaryUpdate = "tag_names = CASE WHEN tag_names IS NULL THEN ? ELSE " + Db.getInstance().concat(new String[] { "tag_names", "?" }) + " END";
/*  413 */           sanityCheckAnd = "(tag_names IS NULL OR tag_names NOT LIKE ?) AND ";
/*      */         } else {
/*  415 */           primaryUpdate = "tag_names = CASE tag_names WHEN ? THEN NULL ELSE REPLACE(tag_names, ?, '\000') END";
/*  416 */           sanityCheckAnd = "";
/*      */         }
/*      */       }
/*      */       
/*  420 */       String updateChangeID = altersModseq ? ", mod_metadata = ?, change_date = ?" : "";
/*      */       
/*  422 */       for (int i = 0; i < itemIds.size(); i += Db.getINClauseBatchSize()) {
/*  423 */         int count = Math.min(Db.getINClauseBatchSize(), itemIds.size() - i);
/*  424 */         stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox) + " SET " + primaryUpdate + updateChangeID + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + sanityCheckAnd + DbUtil.whereIn("id", count));
/*      */         
/*      */ 
/*  427 */         int pos = 1;
/*      */         
/*  429 */         if (isFlag) {
/*  430 */           stmt.setLong(pos++, ((Flag)tag).toBitmask());
/*      */         } else {
/*  432 */           stmt.setString(pos++, delimitTagName(tag.getName()));
/*  433 */           stmt.setString(pos++, delimitTagName(tag.getName()).substring(add ? 1 : 0));
/*      */         }
/*      */         
/*  436 */         if (altersModseq) {
/*  437 */           stmt.setInt(pos++, mbox.getOperationChangeID());
/*  438 */           stmt.setInt(pos++, mbox.getOperationTimestamp());
/*      */         }
/*  440 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*      */         
/*  442 */         if (isFlag) {
/*  443 */           stmt.setLong(pos++, ((Flag)tag).toBitmask());
/*  444 */         } else if (add) {
/*  445 */           stmt.setString(pos++, tagLIKEPattern(tag.getName()));
/*      */         }
/*      */         
/*  448 */         for (int index = i; index < i + count; index++) {
/*  449 */           stmt.setInt(pos++, ((Integer)itemIds.get(index)).intValue());
/*      */         }
/*  451 */         stmt.executeUpdate();
/*  452 */         stmt.close();
/*  453 */         stmt = null;
/*      */         
/*  455 */         if (add) {
/*  456 */           addTaggedItemEntries(mbox, tag.getId(), itemIds.subList(i, i + count));
/*      */         } else {
/*  458 */           removeTaggedItemEntries(mbox, tag.getId(), itemIds.subList(i, i + count));
/*      */         }
/*      */       }
/*      */     } catch (SQLException e) {
/*  462 */       throw ServiceException.FAILURE("updating tag data for " + itemIds.size() + " items: " + DbMailItem.getIdListForLogging(itemIds), e);
/*      */     } finally {
/*  464 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   static void addTaggedItemEntries(Mailbox mbox, int tagId, List<Integer> itemIds) throws ServiceException
/*      */   {
/*  470 */     if ((tagId < 0) && (!Mailbox.REIFIED_FLAGS.contains(Integer.valueOf(tagId)))) {
/*  471 */       return;
/*      */     }
/*  473 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  474 */     PreparedStatement stmt = null;
/*      */     
/*  476 */     String command = Db.supports(Db.Capability.REPLACE_INTO) ? "REPLACE" : "INSERT";
/*  477 */     for (Iterator i$ = itemIds.iterator(); i$.hasNext();) { int itemId = ((Integer)i$.next()).intValue();
/*      */       try {
/*  479 */         stmt = conn.prepareStatement(command + " INTO " + getTaggedItemTableName(mbox) + "(" + DbMailItem.MAILBOX_ID + "tag_id, item_id) VALUES (" + DbMailItem.MAILBOX_ID_VALUE + "?, ?)");
/*      */         
/*  481 */         int pos = 1;
/*  482 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  483 */         stmt.setInt(pos++, tagId);
/*  484 */         stmt.setInt(pos++, itemId);
/*  485 */         stmt.executeUpdate();
/*      */       } catch (SQLException e) {
/*  487 */         throw ServiceException.FAILURE("adding TAGGED_ITEM entries for tag: " + tagId + ", item: " + itemId, e);
/*      */       } finally {
/*  489 */         DbPool.closeStatement(stmt);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   static void removeTaggedItemEntries(Mailbox mbox, int tagId, List<Integer> itemIds) throws ServiceException {
/*  495 */     if ((tagId < 0) && (!Mailbox.REIFIED_FLAGS.contains(Integer.valueOf(tagId)))) {
/*  496 */       return;
/*      */     }
/*  498 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  499 */     PreparedStatement stmt = null;
/*      */     try {
/*  501 */       stmt = conn.prepareStatement("DELETE FROM " + getTaggedItemTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "tag_id = ? AND " + DbUtil.whereIn("item_id", itemIds.size()));
/*      */       
/*  503 */       int pos = 1;
/*  504 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  505 */       stmt.setInt(pos++, tagId);
/*  506 */       for (Iterator i$ = itemIds.iterator(); i$.hasNext();) { int itemId = ((Integer)i$.next()).intValue();
/*  507 */         stmt.setInt(pos++, itemId);
/*      */       }
/*  509 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  511 */       throw ServiceException.FAILURE("removing TAGGED_ITEM entries for tag: " + tagId + ", items: " + DbMailItem.getIdListForLogging(itemIds), e);
/*      */     } finally {
/*  513 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void persistCounts(Tag tag) throws ServiceException {
/*  518 */     Mailbox mbox = tag.getMailbox();
/*      */     
/*  520 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  521 */     PreparedStatement stmt = null;
/*      */     try {
/*  523 */       stmt = conn.prepareStatement("UPDATE " + getTagTableName(mbox) + " SET item_count = ?, unread = ? WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id = ?");
/*      */       
/*  525 */       int pos = 1;
/*  526 */       stmt.setInt(pos++, (int)tag.getSize());
/*  527 */       stmt.setInt(pos++, tag.getUnreadCount());
/*  528 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  529 */       stmt.setInt(pos++, tag.getId());
/*      */       
/*  531 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  533 */       throw ServiceException.FAILURE("updating counts for tag " + tag.getName(), e);
/*      */     } finally {
/*  535 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void saveMetadata(Tag tag) throws ServiceException {
/*  540 */     Mailbox mbox = tag.getMailbox();
/*      */     
/*  542 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  543 */     PreparedStatement stmt = null;
/*      */     try {
/*  545 */       stmt = conn.prepareStatement("UPDATE " + getTagTableName(mbox) + " SET color = ?, policy = ?, listed = ?, sequence = ? WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id = ?");
/*      */       
/*  547 */       int pos = 1;
/*  548 */       Color color = tag.getRgbColor();
/*  549 */       if ((color != null) && (color.getMappedColor() != 0)) {
/*  550 */         stmt.setLong(pos++, color.getValue());
/*      */       } else {
/*  552 */         stmt.setNull(pos++, -5);
/*      */       }
/*  554 */       RetentionPolicy rp = tag.getRetentionPolicy();
/*  555 */       if ((rp != null) && (rp.isSet())) {
/*  556 */         stmt.setString(pos++, RetentionPolicyManager.toMetadata(rp, true).toString());
/*      */       } else {
/*  558 */         stmt.setNull(pos++, 12);
/*      */       }
/*  560 */       stmt.setBoolean(pos++, tag.isListed());
/*  561 */       stmt.setInt(pos++, tag.getModifiedSequence());
/*  562 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  563 */       stmt.setInt(pos++, tag.getId());
/*      */       
/*  565 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  567 */       throw ServiceException.FAILURE("updating counts for tag " + tag.getName(), e);
/*      */     } finally {
/*  569 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void renameTag(Tag tag) throws ServiceException {
/*  574 */     Mailbox mbox = tag.getMailbox();
/*  575 */     String newName = tag.getName();
/*      */     
/*  577 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  578 */     PreparedStatement stmt = null;
/*  579 */     ResultSet rs = null;
/*      */     try
/*      */     {
/*  582 */       stmt = conn.prepareStatement("SELECT name FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id = ?");
/*      */       
/*  584 */       int pos = 1;
/*  585 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  586 */       stmt.setInt(pos++, tag.getId());
/*      */       
/*  588 */       rs = stmt.executeQuery();
/*  589 */       if (!rs.next()) {
/*  590 */         throw MailServiceException.NO_SUCH_TAG(tag.getId());
/*      */       }
/*  592 */       String oldName = rs.getString(1);
/*  593 */       if (newName.equals(oldName)) {
/*      */         return;
/*      */       }
/*  596 */       DbPool.closeResults(rs);
/*  597 */       rs = null;
/*  598 */       DbPool.closeStatement(stmt);
/*  599 */       stmt = null;
/*      */       
/*      */ 
/*  602 */       stmt = conn.prepareStatement("UPDATE " + getTagTableName(mbox) + " SET name = ?, sequence = ? WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id = ?");
/*      */       
/*  604 */       pos = 1;
/*  605 */       stmt.setString(pos++, newName);
/*  606 */       stmt.setInt(pos++, tag.getModifiedSequence());
/*  607 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  608 */       stmt.setInt(pos++, tag.getId());
/*      */       
/*  610 */       stmt.executeUpdate();
/*  611 */       DbPool.closeStatement(stmt);
/*  612 */       stmt = null;
/*      */       
/*      */ 
/*  615 */       if (Db.supports(Db.Capability.MULTITABLE_UPDATE)) {
/*  616 */         String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
/*  617 */         stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "mi.id = ti.item_id" + " SET tag_names = REPLACE(tag_names, ?, ?), mod_metadata = ?, change_date = ?" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = ?");
/*      */         
/*      */ 
/*      */ 
/*  621 */         pos = 1;
/*  622 */         stmt.setString(pos++, delimitTagName(oldName));
/*  623 */         stmt.setString(pos++, delimitTagName(newName));
/*  624 */         stmt.setInt(pos++, mbox.getOperationChangeID());
/*  625 */         stmt.setInt(pos++, mbox.getOperationTimestamp());
/*  626 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  627 */         stmt.setInt(pos++, tag.getId());
/*      */       } else {
/*  629 */         stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox) + " SET tag_names = REPLACE(tag_names, ?, ?), mod_metadata = ?, change_date = ?" + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id IN" + " (SELECT ti.item_id FROM " + getTaggedItemTableName(mbox, "ti") + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = ?)");
/*      */         
/*      */ 
/*      */ 
/*  633 */         pos = 1;
/*  634 */         stmt.setString(pos++, delimitTagName(oldName));
/*  635 */         stmt.setString(pos++, delimitTagName(newName));
/*  636 */         stmt.setInt(pos++, mbox.getOperationChangeID());
/*  637 */         stmt.setInt(pos++, mbox.getOperationTimestamp());
/*  638 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  639 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  640 */         stmt.setInt(pos++, tag.getId());
/*      */       }
/*      */       
/*  643 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  645 */       if (Db.errorMatches(e, Db.Error.DUPLICATE_ROW)) {
/*  646 */         throw MailServiceException.ALREADY_EXISTS(newName, e);
/*      */       }
/*  648 */       throw ServiceException.FAILURE("renaming tag " + tag.getId() + " to " + tag.getName(), e);
/*      */     }
/*      */     finally {
/*  651 */       DbPool.closeResults(rs);
/*  652 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void deleteTag(Tag tag) throws ServiceException {
/*  657 */     Mailbox mbox = tag.getMailbox();
/*      */     
/*  659 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  660 */     PreparedStatement stmt = null;
/*      */     
/*      */     try
/*      */     {
/*  664 */       int pos = 1;
/*  665 */       String delimited = delimitTagName(tag.getName());
/*  666 */       if (Db.supports(Db.Capability.MULTITABLE_UPDATE)) {
/*  667 */         String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
/*  668 */         stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "mi.id = ti.item_id" + " SET mi.tag_names = CASE mi.tag_names WHEN ? THEN NULL ELSE REPLACE(mi.tag_names, ?, '" + Db.getEscapeSequence() + "0') END, mod_metadata = ?, change_date = ?" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = ?");
/*      */         
/*      */ 
/*      */ 
/*  672 */         stmt.setString(pos++, delimited);
/*  673 */         stmt.setString(pos++, delimited);
/*  674 */         stmt.setInt(pos++, mbox.getOperationChangeID());
/*  675 */         stmt.setInt(pos++, mbox.getOperationTimestamp());
/*  676 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  677 */         stmt.setInt(pos++, tag.getId());
/*      */       } else {
/*  679 */         stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox) + " SET tag_names = CASE tag_names WHEN ? THEN NULL ELSE REPLACE(tag_names, ?, '" + Db.getEscapeSequence() + "0') END, mod_metadata = ?, change_date = ?" + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id IN" + " (SELECT ti.item_id FROM " + getTaggedItemTableName(mbox, "ti") + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = ?)");
/*      */         
/*      */ 
/*      */ 
/*  683 */         stmt.setString(pos++, delimited);
/*  684 */         stmt.setString(pos++, delimited);
/*  685 */         stmt.setInt(pos++, mbox.getOperationChangeID());
/*  686 */         stmt.setInt(pos++, mbox.getOperationTimestamp());
/*  687 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  688 */         pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  689 */         stmt.setInt(pos++, tag.getId());
/*      */       }
/*      */       
/*  692 */       stmt.executeUpdate();
/*  693 */       DbPool.closeStatement(stmt);
/*  694 */       stmt = null;
/*      */       
/*      */ 
/*  697 */       deleteTagRow(mbox, tag.getId());
/*      */     } catch (SQLException e) {
/*  699 */       throw ServiceException.FAILURE("updating counts for tag " + tag.getName(), e);
/*      */     } finally {
/*  701 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static void deleteTagRow(Mailbox mbox, int tagId) throws ServiceException {
/*  706 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  707 */     PreparedStatement stmt = null;
/*      */     try
/*      */     {
/*  710 */       stmt = conn.prepareStatement("DELETE FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id = ?");
/*      */       
/*  712 */       int pos = 1;
/*  713 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  714 */       stmt.setInt(pos++, tagId);
/*  715 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  717 */       throw ServiceException.FAILURE("deleting tag row for tagId " + tagId, e);
/*      */     } finally {
/*  719 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static MailItem.PendingDelete getLeafNodes(Mailbox mbox, Tag tag, int before, Integer maxItems) throws ServiceException
/*      */   {
/*  725 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*      */     
/*  727 */     PreparedStatement stmt = null;
/*  728 */     ResultSet rs = null;
/*      */     try {
/*  730 */       String orderByLimit = "";
/*  731 */       if ((maxItems != null) && (Db.supports(Db.Capability.LIMIT_CLAUSE))) {
/*  732 */         orderByLimit = " ORDER BY date " + Db.getInstance().limit(maxItems.intValue());
/*      */       }
/*      */       
/*  735 */       String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
/*  736 */       stmt = conn.prepareStatement("SELECT id, size, type, unread, folder_id, parent_id, blob_digest, mod_content, mod_metadata, flags, index_id, locator, tag_names, uuid FROM " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "ti.item_id = mi.id" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = ? AND date < ? AND type NOT IN " + DbMailItem.NON_SEARCHABLE_TYPES + orderByLimit);
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*  741 */       int pos = 1;
/*  742 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*  743 */       stmt.setInt(pos++, tag.getId());
/*  744 */       stmt.setInt(pos++, before);
/*      */       
/*  746 */       MailItem.PendingDelete info = DbMailItem.accumulateDeletionInfo(mbox, stmt);
/*  747 */       stmt = null;
/*  748 */       return info;
/*      */     } catch (SQLException e) {
/*  750 */       throw ServiceException.FAILURE("fetching list of items for purge", e);
/*      */     } finally {
/*  752 */       DbPool.closeResults(rs);
/*  753 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   private static void recalculateTagStrings(Mailbox mbox, Tag taggedWith)
/*      */     throws ServiceException
/*      */   {
/*  760 */     DbPool.DbConnection conn = mbox.getOperationConnection();
/*  761 */     PreparedStatement stmt = null;
/*      */     try {
/*  763 */       stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox) + " SET tag_names = " + "(SELECT (CONCAT('" + Db.getEscapeSequence() + "0', GROUP_CONCAT(t.name SEPARATOR '" + Db.getEscapeSequence() + "0'), '" + Db.getEscapeSequence() + "0')" + " FROM tag t INNER JOIN tagged_item ti ON t.mailbox_id = ti.mailbox_id AND t.id = ti.tag_id" + " WHERE ti.item_id = mi.id AND ti.tag_id > 0)" + (DebugConfig.disableMailboxGroups ? "" : " WHERE mi.mailbox_id = ?"));
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  769 */       int pos = 1;
/*  770 */       pos = DbMailItem.setMailboxId(stmt, mbox, pos);
/*      */       
/*  772 */       stmt.executeUpdate();
/*      */     } catch (SQLException e) {
/*  774 */       throw ServiceException.FAILURE("recalculating TAG_NAMES column for mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  776 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   public static String inThisMailboxAnd(String alias) {
/*  781 */     if (DebugConfig.disableMailboxGroups) {
/*  782 */       return "";
/*      */     }
/*  784 */     StringBuilder sb = new StringBuilder();
/*  785 */     if (!Strings.isNullOrEmpty(alias)) {
/*  786 */       sb.append(alias).append('.');
/*      */     }
/*  788 */     return "mailbox_id = ? AND ";
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public static String getTagTableName(int mailboxId, int groupId)
/*      */   {
/*  795 */     return DbMailbox.qualifyTableName(groupId, "tag");
/*      */   }
/*      */   
/*      */   public static String getTagTableName(MailItem item) {
/*  799 */     return DbMailbox.qualifyTableName(item.getMailbox(), "tag");
/*      */   }
/*      */   
/*      */   public static String getTagTableName(Mailbox mbox) {
/*  803 */     return DbMailbox.qualifyTableName(mbox, "tag");
/*      */   }
/*      */   
/*      */   public static String getTagTableName(Mailbox mbox, String alias) {
/*  807 */     return getTagTableName(mbox) + " AS " + alias;
/*      */   }
/*      */   
/*      */   public static String getTaggedItemTableName(int mailboxId, int groupId) {
/*  811 */     return DbMailbox.qualifyTableName(groupId, "tagged_item");
/*      */   }
/*      */   
/*      */   public static String getTaggedItemTableName(Mailbox mbox) {
/*  815 */     return DbMailbox.qualifyTableName(mbox, "tagged_item");
/*      */   }
/*      */   
/*      */   public static String getTaggedItemTableName(Mailbox mbox, String alias) {
/*  819 */     return getTaggedItemTableName(mbox) + " AS " + alias;
/*      */   }
/*      */   
/*      */   @VisibleForTesting
/*      */   public static void debugConsistencyCheck(Mailbox mbox) throws ServiceException
/*      */   {
/*  825 */     DbPool.DbConnection conn = mbox.lock.isWriteLockedByCurrentThread() ? mbox.getOperationConnection() : DbPool.getConnection(mbox);
/*      */     
/*  827 */     PreparedStatement stmt = null;
/*  828 */     ResultSet rs = null;
/*      */     try {
/*  830 */       Map<Integer, MailItem.UnderlyingData> tdata = Maps.newHashMap();
/*      */       
/*  832 */       stmt = conn.prepareStatement("SELECT id, name, color, item_count, unread, listed, sequence, policy FROM " + getTagTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "id > 0");
/*      */       
/*  834 */       DbMailItem.setMailboxId(stmt, mbox, 1);
/*      */       
/*  836 */       rs = stmt.executeQuery();
/*  837 */       while (rs.next()) {
/*  838 */         MailItem.UnderlyingData data = asUnderlyingData(rs);
/*  839 */         tdata.put(Integer.valueOf(data.id), data);
/*      */       }
/*  841 */       DbPool.closeResults(rs);
/*  842 */       rs = null;
/*  843 */       DbPool.closeStatement(stmt);
/*  844 */       stmt = null;
/*      */       
/*      */ 
/*  847 */       validateTaggedItem(conn, mbox, tdata);
/*      */       
/*  849 */       verifyTaggedItem(conn, mbox, tdata);
/*      */       
/*  851 */       verifyTagCounts(conn, mbox, tdata);
/*      */     } catch (SQLException e) {
/*  853 */       throw ServiceException.FAILURE("consistency checking tags/flags for mailbox " + mbox.getId(), e);
/*      */     } finally {
/*  855 */       DbPool.closeResults(rs);
/*  856 */       DbPool.closeStatement(stmt);
/*  857 */       if (!mbox.lock.isWriteLockedByCurrentThread()) {
/*  858 */         conn.close();
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   private static void validateTaggedItem(DbPool.DbConnection conn, Mailbox mbox, Map<Integer, MailItem.UnderlyingData> tdata) throws ServiceException
/*      */   {
/*  865 */     PreparedStatement stmt = null;
/*  866 */     ResultSet rs = null;
/*      */     try {
/*  868 */       String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "ti.mailbox_id = mi.mailbox_id AND ";
/*  869 */       stmt = conn.prepareStatement("SELECT mi.id, mi.type, ti.tag_id  FROM " + getTaggedItemTableName(mbox, "ti") + " INNER JOIN " + DbMailItem.getMailItemTableName(mbox, "mi") + " ON " + mailboxesMatchAnd + "mi.id = ti.item_id" + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "type IN " + DbMailItem.NON_SEARCHABLE_TYPES);
/*      */       
/*      */ 
/*      */ 
/*  873 */       DbMailItem.setMailboxId(stmt, mbox, 1);
/*      */       
/*  875 */       rs = stmt.executeQuery();
/*  876 */       while (rs.next()) {
/*  877 */         int itemId = rs.getInt(1);int type = rs.getInt(2);int tagId = rs.getInt(3);
/*  878 */         String tname = tagId > 0 ? ((MailItem.UnderlyingData)tdata.get(Integer.valueOf(tagId))).name : mbox.getFlagById(tagId).getName();
/*  879 */         Assert.fail("found tag " + tname + " on " + MailItem.Type.of((byte)type) + " " + itemId);
/*      */       }
/*      */     } catch (SQLException e) {
/*  882 */       throw ServiceException.FAILURE("validating TAGGED_ITEM entries", e);
/*      */     } finally {
/*  884 */       DbPool.closeResults(rs);
/*  885 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   private static void verifyTaggedItem(DbPool.DbConnection conn, Mailbox mbox, Map<Integer, MailItem.UnderlyingData> tdata) throws ServiceException
/*      */   {
/*  891 */     int flagMask = 0;
/*  892 */     for (Iterator i$ = Mailbox.REIFIED_FLAGS.iterator(); i$.hasNext();) { int flagId = ((Integer)i$.next()).intValue();
/*  893 */       flagMask |= 1 << -flagId - 1;
/*      */     }
/*      */     
/*  896 */     PreparedStatement stmt = null;
/*  897 */     ResultSet rs = null;
/*      */     try {
/*  899 */       stmt = conn.prepareStatement("SELECT id, flags, tag_names, unread FROM " + DbMailItem.getMailItemTableName(mbox) + " WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "type NOT IN " + DbMailItem.NON_SEARCHABLE_TYPES);
/*      */       
/*  901 */       DbMailItem.setMailboxId(stmt, mbox, 1);
/*      */       
/*  903 */       rs = stmt.executeQuery();
/*  904 */       while (rs.next()) {
/*  905 */         int id = rs.getInt(1);
/*  906 */         int flags = rs.getInt(2) & flagMask | (rs.getBoolean(4) ? Flag.BITMASK_UNREAD : 0);
/*  907 */         String[] tagset = deserializeTags(rs.getString(3));
/*  908 */         Set<String> tags = tagset == null ? Collections.emptySet() : Sets.newHashSet(tagset);
/*      */         
/*  910 */         PreparedStatement stmtcheck = null;
/*  911 */         ResultSet rscheck = null;
/*      */         try
/*      */         {
/*  914 */           String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "tag.mailbox_id = ti.mailbox_id AND ";
/*  915 */           stmtcheck = conn.prepareStatement("SELECT id, name FROM " + getTagTableName(mbox, "tag") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "tag.id = ti.tag_id" + " WHERE " + inThisMailboxAnd("ti") + "ti.item_id = ?");
/*      */           
/*      */ 
/*  918 */           int pos = 1;
/*  919 */           pos = DbMailItem.setMailboxId(stmtcheck, mbox, pos);
/*  920 */           stmtcheck.setInt(pos++, id);
/*      */           
/*  922 */           rscheck = stmtcheck.executeQuery();
/*  923 */           int flagcheck = 0;
/*  924 */           Set<String> tagcheck = Sets.newHashSetWithExpectedSize(tags.size());
/*  925 */           while (rscheck.next()) {
/*  926 */             int idcheck = rscheck.getInt(1);
/*  927 */             String namecheck = rscheck.getString(2);
/*  928 */             if (idcheck < 0) {
/*  929 */               flagcheck |= 1 << -idcheck - 1;
/*      */             } else {
/*  931 */               tagcheck.add(namecheck);
/*      */             }
/*      */           }
/*  934 */           Assert.assertEquals("flags for item " + id, flags, flagcheck);
/*  935 */           Assert.assertEquals("tags for item " + id, tags, tagcheck);
/*      */         } catch (SQLException e) {
/*  937 */           throw ServiceException.FAILURE("consistency checking TAGGED_ITEM vs. MAIL_ITEM", e);
/*      */         }
/*      */         finally {}
/*      */       }
/*      */     }
/*      */     catch (SQLException e)
/*      */     {
/*  944 */       throw ServiceException.FAILURE("consistency checking TAGGED_ITEM vs. MAIL_ITEM", e);
/*      */     } finally {
/*  946 */       DbPool.closeResults(rs);
/*  947 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   private static void verifyTagCounts(DbPool.DbConnection conn, Mailbox mbox, Map<Integer, MailItem.UnderlyingData> tdata) throws ServiceException
/*      */   {
/*  953 */     PreparedStatement stmt = null;
/*  954 */     ResultSet rs = null;
/*      */     try {
/*  956 */       String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "ti.mailbox_id = mi.mailbox_id AND ";
/*  957 */       stmt = conn.prepareStatement("SELECT ti.tag_id, COUNT(ti.item_id), " + Db.clauseIFNULL("SUM(mi.unread)", "0") + " FROM " + getTaggedItemTableName(mbox, "ti") + " INNER JOIN " + DbMailItem.getMailItemTableName(mbox, "mi") + " ON " + mailboxesMatchAnd + "mi.id = ti.item_id" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id > 0 AND " + Db.getInstance().bitAND("mi.flags", String.valueOf(Flag.BITMASK_DELETED)) + " = 0" + " GROUP BY ti.tag_id");
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*  962 */       DbMailItem.setMailboxId(stmt, mbox, 1);
/*      */       
/*  964 */       rs = stmt.executeQuery();
/*  965 */       Map<Integer, MailItem.UnderlyingData> tcheck = new HashMap(tdata);
/*  966 */       while (rs.next()) {
/*  967 */         int id = rs.getInt(1);int size = rs.getInt(2);int unread = rs.getInt(3);
/*  968 */         MailItem.UnderlyingData data = (MailItem.UnderlyingData)tcheck.remove(Integer.valueOf(id));
/*  969 */         Assert.assertNotNull("no TAG row for id " + id, data);
/*  970 */         Assert.assertEquals("size for tag " + data.name, size, data.size);
/*  971 */         Assert.assertEquals("unread for tag " + data.name, unread, data.unreadCount);
/*      */       }
/*  973 */       for (MailItem.UnderlyingData data : tcheck.values()) {
/*  974 */         Assert.assertEquals("size for tag " + data.name, 0L, data.size);
/*  975 */         Assert.assertEquals("unread for tag " + data.name, 0, data.unreadCount);
/*      */       }
/*      */     } catch (SQLException e) {
/*  978 */       throw ServiceException.FAILURE("consistency checking TAGGED_ITEM vs. TAG", e);
/*      */     } finally {
/*  980 */       DbPool.closeResults(rs);
/*  981 */       DbPool.closeStatement(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static <T> void assertCollectionsEqual(String msg, Collection<T> expected, Collection<T> actual)
/*      */   {
/* 1014 */     String prefix = msg + ": ";
/* 1015 */     if (expected == null) {
/* 1016 */       Assert.assertNull(prefix + "expected <null>", actual);
/* 1017 */     } else if (actual == null) {
/* 1018 */       Assert.fail(prefix + "was <null>, expected " + expected);
/*      */     } else {
/* 1020 */       Assert.assertEquals(prefix + "collection size", expected.size(), actual.size());
/* 1021 */       for (T t : expected) {
/* 1022 */         if (!actual.contains(t)) {
/* 1023 */           Assert.fail(prefix + "actual collection does not contain <" + t + ">");
/*      */         }
/*      */       }
/*      */     }
/*      */   }
/*      */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/db/DbTag.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */